#include "Generate_C_std.h"
#include "Pattern_method_definition.h"

bool Pattern_method_definition::match(Statement* that)
{
  use_scope = false;
  pattern = new Wildcard<Method>;
  return that->match(pattern);
}

void Pattern_method_definition::generate_code (Generate_C* gen)
{
  signature = pattern->value->signature;
  
  method_entry(gen);
  buf << 		increment_stat(demangle (this));
  
  // Use a different gen for the nested function
  Generate_C* new_gen = new Generate_C(buf);
  new_gen->micg = gen->micg;
  new_gen->visit_statement_list (pattern->value->statements);
  buf << new_gen->body.str ();
  
  method_exit();
}


void Pattern_method_definition::debug_argument_stack()
{
  buf	
    << "{ /**debug_argument_stack */\n"
    <<		"void **p;\n"
    <<		"int arg_count;\n"
    <<		"zvalParam param_ptr;\n"
    <<	"\n"
    <<		"p = EG(argument_stack).top_element-2;\n"
    <<		"arg_count = (ulong) *p;\n"
    <<	"\n"
    <<		"printf(\"\\nARGUMENT STACK\\n\");\n"
    <<		"while (arg_count > 0)\n"
    <<		"{\n"
    <<		"	param_ptr = *(p-arg_count);\n"
    <<		"	printf(\"addr = %08X, refcount = %d, is_ref = %d\\n\","
    <<  "(long)param_ptr, param_ptr->refcount, param_ptr->is_ref);\n"
    <<		"	arg_count--;\n"
    <<		"}\n"
    <<		"printf(\"END ARGUMENT STACK\\n\");\n"
    << "}\n"
    ;
}

void Pattern_method_definition::method_entry(Generate_C* gen)
{
  String* class_name = NULL;
  if(signature->attrs->has("phc.codegen.class_name"))
    class_name = signature->attrs->get_string("phc.codegen.class_name");
	
  // Function header
  if(class_name != NULL)
    {
      buf
	<< "PHP_METHOD(" << *class_name << ", "
	<< *signature->method_name->value << ")\n";
    }
  else
    {
      buf
	<< "PHP_FUNCTION"
	<< "(" << *signature->method_name->value << ")\n";
    }
		
  buf << "{\n";

  // __MAIN__ uses the global symbol table. Dont allocate for
  // functions which dont need a symbol table.
  if (*signature->method_name->value != "__MAIN__" 
      && not signature->method_name->attrs->is_true ("phc.codegen.st_entry_not_required"))
    {
      buf
	<< "// Setup locals array\n"
	<< "HashTableT locals;\n"
	//	<< "ALLOC_HASHTABLE(locals);\n"
	//	<< "zend_hash_init(locals, 64, NULL, ZVAL_PTR_DTOR, 0);\n"
	<< "HashTableT old_active_symbol_table = EG(active_symbol_table);\n"
	<< "EG(active_symbol_table) = locals;\n"
	;
    }

  // Declare variables which can go outside the symbol table
  String_list* var_names = dyc<String_list> (pattern->value->attrs->get ("phc.codegen.non_st_vars"));
  foreach (String* var, *var_names)
    {
      if(*var == "this") 
	buf << "zvalT " << get_non_st_name (var) << " = getThis();\n";
      else
	buf << "zvalT " << get_non_st_name (var) << " = NULL;\n";
    }

  // Declare hashtable iterators for the function
  String_list* iterators = dyc<String_list> (pattern->value->attrs->get ("phc.codegen.ht_iterators"));
  foreach (String* iter, *iterators)
    buf << "HashPosition " << *iter << ";\n";

  // debug_argument_stack();

  // TODO: the same variable may be used twice in the signature. This leads to a memory leak.
  Formal_parameter_list* parameters = signature->formal_parameters;
  if(parameters && parameters->size() > 0)
    {
      buf 
	<< "// Add all parameters as local variables\n"
	<< "{\n"
	<< "int num_args = ZEND_NUM_ARGS ();\n"
	<< "zvalParams<" << parameters->size() << "> params(" << parameters->size() << ");\n"
	// First parameter to zend_get_parameters_array does not appear
	// to be used (by looking at the source)
	<< "zend_get_parameters_array(0, num_args, params);\n"
	;

      int index = 0;
      foreach (Formal_parameter* param, *parameters)
	{
	  //				buf << "printf(\"refcount = %d, is_ref = %d\\n\", params[" << index << "]->refcount, params[" << index << "]->is_ref);\n";
	  buf << "// param " << index << "\n";

	  // if a default value is available, then create code to
	  // assign it if an argument is not provided at run time.
	  // We model it as an assignment to the named variable,
	  // and call on the code generator to generate the
	  // default assignment for us.
	  if (param->var->default_value)
	    {
	      buf 
		<< "if (num_args <= " << index << ")\n"
		<< "{\n"
		;

	      gen->compile_static_value ("default_value", buf, param->var->default_value);

	      buf
		<< "default_value.Free();\n"
		<<	"	params.Reset(" << index << ",default_value);\n"
		<< "}\n"
		;
	    }

	  buf
	    << "params.AddRef(" << index << ");\n";

	  // TODO this should be abstactable, but it work now, so
	  // leave it.
				
	  // We can have multiple parameters with the same name. In that
	  // case, destroy the predecessor (the second is not deemed to
	  // assign to the first, so references etc are moot).
	  if (param->var->variable_name->attrs->is_true ("phc.codegen.st_entry_not_required"))
	    {
	      string name = get_non_st_name (param->var->variable_name);
	      buf 
		<< "	zval_ptr_dtor (" << name << ");\n"
		<< name << " = params.Get(" << index << ");\n";
	    }
	  else
	    {
	      buf
		<< "zend_hash_del (EG(active_symbol_table), "
		<<		"\"" << *param->var->variable_name->value << "\", " 
		<<		param->var->variable_name->value->length() + 1 << ");\n"

		<< "zend_hash_quick_add(EG(active_symbol_table), "
		<<		"\"" << *param->var->variable_name->value << "\", " 
		<<		param->var->variable_name->value->length() + 1 << ", "
		<<		get_hash (param->var->variable_name) << ", "
		<<		"params.Get(" << index << "));\n"
		;
	    }

	  index++;
	}
				
      buf << "}\n";

    }
		
  buf << "// Function body\n";
}

void Pattern_method_definition::method_exit()
{
  buf
    // Labels are local to a function
    << "// Method exit\n"
    << "end_of_function:__attribute__((unused));\n"
    ;


  if (*signature->method_name->value != "__MAIN__"
      && not signature->method_name->attrs->is_true ("phc.codegen.st_entry_not_required"))
    {
      buf
	<< "// Destroy locals array\n"
	<< "zend_hash_destroy(locals);\n"
	<< "FREE_HASHTABLE(locals);\n"
	<< "EG(active_symbol_table) = old_active_symbol_table;\n"
	;
    }

  // Cleanup local variables
  String_list* var_names = dyc<String_list> (pattern->value->attrs->get ("phc.codegen.non_st_vars"));
  assert (var_names);
  foreach (String* var_name, *var_names)
    {
      // don't destroy $this
      if(*var_name == "this") continue;

      string name = get_non_st_name (var_name);
      buf	<<		"zval_ptr_dtor (" << name << ");\n";
	;
    }

  // See comment in Method_invocation. We save the refcount of
  // return_by_reference. Note that we get the wrong answer if we do this
  // before the destructors have run, since we can't tell how many
  // destructors will affect it.
  if (signature->is_ref)
    {
      buf
	<< "if (*return_value_ptr)\n"
	<< "	saved_refcount = (*return_value_ptr)->refcount;\n"
	;
    }

  buf << "}\n" ;
}


CREATE(Pattern_method_definition);
